#include "PointLightShadowRenderPass.h"
#include <SERender/Resourses/Loader/TextureLoader.h>
#include <SECore/Manager.h>
#include <SEngine/Render/Descriptors/SceneDescriptor.h>
#include <SERender/Descriptors/FrameDescriptor.h>
#include <SEngine/Render/RenderFeature/ModelRenderFeature.h>
#include <SEngine/Ecs/Components/TransformComp.h>
#include <SEngine/Ecs/Components/LightComp.h>
#include <SEngine/Ecs/Components/ModelRenderComp.h>
#include <SEngine/Render/RenderFeature/SkyBoxRenderFeature.h>
#include <SEngine/Render/RenderFeature/UtilRenderFeature.h>
#include <SEForwardRenderPiplinePlugin/RenderTargetManager.h>
#include <SEUi/SEUi.h>

namespace SEngine
{
    PointLightShadowRenderPass::PointLightShadowRenderPass(Renderer &renderer)
        : RenderPass(renderer)
    {
        m_fbo = std::make_unique<FrameBuffer>();

        TextureLoader<TextureCube> cubeTexLoader;
        cubeTexLoader.SetInternalFormat(DEPTH_COMPONENT);
        cubeTexLoader.SetFormat(DEPTH_COMPONENT);
        cubeTexLoader.SetType(UNSIGNED_BYTE);
        cubeTexLoader.SetParameter(TEXTURE_MAG_FILTER, LINEAR);
        cubeTexLoader.SetParameter(TEXTURE_MIN_FILTER, LINEAR);
        cubeTexLoader.SetParameter(TEXTURE_WRAP_S, CLAMP_TO_EDGE);
        cubeTexLoader.SetParameter(TEXTURE_WRAP_T, CLAMP_TO_EDGE);
        cubeTexLoader.SetParameter(TEXTURE_WRAP_R, CLAMP_TO_EDGE);
        m_lightDepthTex = cubeTexLoader.CreateTexture(1024, 1024);

        // 渲染Cube深度图(6张)
        m_cubeDepthMaterial = Manager::GetInstance().Get<Material>("CubeDepth").value();
        m_cubeDepthMaterial.Value().GetPSO().depth = true;

        m_cubeDepthShadowMaterial = Manager::GetInstance().Get<Material>("PointLightShadow").value();
        m_cubeDepthShadowMaterial.Value().GetPSO().blend = true;
        m_cubeDepthShadowMaterial.Value().GetPSO().depth = false;
    }

    void PointLightShadowRenderPass::BeginFrame() 
    {

    }

    void PointLightShadowRenderPass::DrawFrame()
    {   
        auto & rtManager = m_renderer.AnyDescriptor::GetDescriptor<RenderTargetManager>()->get();
        auto & sceneDes = m_renderer.AnyDescriptor::GetDescriptor<SceneDescriptor>()->get();
        auto & frameDes = m_renderer.AnyDescriptor::GetDescriptor<FrameDescriptor>()->get();
        auto & modelFeature = m_renderer.Descriptor<RenderFeature>::GetDescriptor<ModelRenderFeature>()->get();
        auto & utilRenderFeature = m_renderer.Descriptor<RenderFeature>::GetDescriptor<UtilRenderFeature>()->get();


        SignalUpdateUI.AddSlot([this]() {
            if (ImGui::CollapsingHeader("PointLightShadowRenderPass")) {
                ImGui::DragFloat("Bias", &this->m_bias, 0.01f, 0.0f, 1.0f);
                ImGui::DragFloat("Samples", &this->m_samples, 1.0f, 1.0f, 3.0f);
            }
            return SlotState::Once;
        });

        sceneDes.scene->Components<PointLightComp, TransformComp>([&](Actor & actor, PointLightComp & ptLight, TransformComp & transComp) {
            m_renderer.GetDrive()->Viewport(0, 0, 1024, 1024); 
            auto & light = ptLight.light;
            std::vector<glm::vec3> fronts{
                glm::vec3{1.0f, 0.0f, 0.0f},
                glm::vec3{-1.0f, 0.0f, 0.0f},
                glm::vec3{0.0f, 1.0f, 0.0f},
                glm::vec3{0.0f, -1.0f, 0.0f},
                glm::vec3{0.0f, 0.0f, 1.0f},
                glm::vec3{0.0f, 0.0f, -1.0f},
            };
            std::vector<glm::vec3> ups{
                glm::vec3{0.0f, -1.0f, 0.0f},
                glm::vec3{0.0f, -1.0f, 0.0f},
                glm::vec3{0.0f, 0.0f, 1.0f},
                glm::vec3{0.0f, 0.0f, -1.0f},
                glm::vec3{0.0f, -1.0f, 0.0f},
                glm::vec3{0.0f, -1.0f, 0.0f},

            };
            const float zFar = 500.0f;
            for (int i = 0; i < 6; ++i) {
                Camera camera;
                camera.SetFov(90.0f);
                camera.SetPos(light.GetPos());
                camera.SetZNear(0.1f);
                camera.SetZFar(zFar);
                camera.SetUp(ups[i]);
                camera.SetFront(fronts[i]);
                auto vp = camera.GetProjectMatrix(1024, 1024) * camera.GetViewMatrix();
                m_cubeDepthMaterial.Value().Set("u_shadowMat[" + std::to_string(i) + "]", vp);
            }
            m_cubeDepthMaterial.Value().Set("u_lightPos", light.GetPos());
            m_cubeDepthMaterial.Value().Set("u_zFar", zFar);
            m_renderer.UseFrameBuffer(*m_fbo, [&]() {
                m_fbo->ClearAttachments();
                m_fbo->SwapAttachments(Attachment::DEPTH_ATTACHMENT, *m_lightDepthTex);
                m_fbo->EnableRenderBuffer(false);
                m_fbo->Resize(1024, 1024);
                m_fbo->Use();
                m_renderer.GetDrive()->Clear(DEPTH_BUFFER_BIT);
                sceneDes.scene->Components<ModelRenderComp>([&](Actor & actor, ModelRenderComp & modelComp) {
                    modelFeature.DrawModel(modelComp.m_model, m_cubeDepthMaterial);
                });
            });
            m_cubeDepthShadowMaterial.Value().Set("u_cameraPositionMap", Shader::Sampler{*rtManager.GetTarget("PositionRT"), TextureSlot::TEXTURE0});
            m_cubeDepthShadowMaterial.Value().Set("u_lightDepthMap", Shader::Sampler{*m_lightDepthTex, TextureSlot::TEXTURE1});
            m_cubeDepthShadowMaterial.Value().Set("u_lightPos", light.GetPos());
            m_cubeDepthShadowMaterial.Value().Set("u_zFar", zFar);
            m_cubeDepthShadowMaterial.Value().Set("u_bias", this->m_bias);
            m_cubeDepthShadowMaterial.Value().Set("u_sample", this->m_samples);
            m_renderer.GetDrive()->Viewport(frameDes.x, frameDes.y, frameDes.w, frameDes.h);
            utilRenderFeature.DrawBillboard(m_cubeDepthShadowMaterial);
        });

    }

}